package com.microstation.controller.system;

import com.microstation.common.Assist;
import com.microstation.common.Result;
import com.microstation.entity.po.AdminInfo;
import com.microstation.entity.po.MenuInfo;
import com.microstation.entity.po.RoleInfo;
import com.microstation.enums.ResultEnum;
import com.microstation.service.AdminInfoService;
import com.microstation.service.MenuInfoService;
import com.microstation.service.RoleInfoService;
import com.microstation.util.*;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author XuCong
 * @date 2018-02-15
 */
@RestController
@RequestMapping("/api/login")
public class LoginController {
    private final static MD5 md5 = new MD5();
    private final static Logger logger = LoggerFactory.getLogger(LoginController.class);
    @Autowired
    private AdminInfoService adminInfoService;
    @Autowired
    private RoleInfoService roleInfoService;
    @Autowired
    private MenuInfoService menuInfoService;

    /**
     * 校验是否已经登录帐号
     *
     * @return
     */
    @RequestMapping(value = "/checkLogin")
    public Result checkLogin() {
        try {
            Subject subject = SecurityUtils.getSubject();
            Session session = subject.getSession();
            if (session.getAttribute("adminLogin") != null) {
                return new Result(false, ResultEnum.SUCCESS.getType(), ResultEnum.SUCCESS.getInfo(),
                        "index1.html");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new Result(true, ResultEnum.NOSESSION.getType(), ResultEnum.NOSESSION.getInfo(), null);
    }

    /**
     * 创建密钥
     *
     * @return
     */
    @RequestMapping(value = "/createKey", method = RequestMethod.POST)
    public Result createKey() {
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        Map<String, Object> map = new HashMap<String, Object>();
        RSAUtils rsa = new RSAUtils();
        // 生成公钥和密钥
        Map<String, Object> keyMap = rsa.createKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyMap.get("publicKey");
        RSAPrivateKey privateKey = (RSAPrivateKey) keyMap.get("privateKey");
        try {
            // js通过模和公钥指数获取公钥对字符串进行加密，注意必须转为16进制
            // 模
            String Modulus = publicKey.getModulus().toString(16);
            // 公钥指数
            String Exponent = publicKey.getPublicExponent().toString(16);
            // 私钥指数
            String private_exponent = privateKey.getPrivateExponent().toString();
            // java中的模和私钥指数不需要转16进制，但是js中的需要转换为16进制
            session.setAttribute("Modulus", publicKey.getModulus().toString());
            session.setAttribute("private_exponent", private_exponent);
            map.put("errType", "true");
            map.put("Exponent", Exponent);
            map.put("Modulus", Modulus);
            return new Result(false, ResultEnum.SUCCESS.getType(), map);
        } catch (Exception e) {
            logger.info(ResultEnum.ERROR.getType(), e);
            return new Result(true, ResultEnum.ERROR.getType(), ResultEnum.ERROR.getInfo(), null);
        }
    }

    /**
     * 用户登录
     *
     * @param request
     * @param adminInfo
     * @return
     */
    @RequestMapping(value = "/adminLogin", method = RequestMethod.POST)
    public Result adminLogin(HttpServletRequest request, AdminInfo adminInfo) {
        UsernamePasswordToken token = null;
        Subject currentUser = SecurityUtils.getSubject();
        try {
            if (StringUtil.isEmpty(adminInfo.getAccount()) || StringUtil.isEmpty(adminInfo.getPassword())) {
                return new Result(true, ResultEnum.FAIL.getType(), "请输入用户名、密码", null);
            } else {
                Session session = currentUser.getSession();
                RSAUtils rsa = new RSAUtils();
                String Modulus = (String) session.getAttribute("Modulus");
                String private_exponent = (String) session.getAttribute("private_exponent");
                // 根据模和私钥指数获取私钥
                RSAPrivateKey prkey = RSAUtils.getPrivateKey(Modulus, private_exponent);
                // System.out.println("====:" + Modulus);
                // System.out.println("====:" + private_exponent);
                // session.invalidate();
                // session = request.getSession();
                // rsa解码
                String password = rsa.decrypttoStr(prkey, adminInfo.getPassword());
                // System.out.println(password);
                token = new UsernamePasswordToken(adminInfo.getAccount(), md5.getMD5ofStr(password));
                try {
                    //帐号不匹配会报异常
                    currentUser.login(token);
                    String ip = IpUtil.getIpAddr(request);
                    String salt = RandomStringUtils.randomAlphanumeric(8);

                    AdminInfo queryParam = new AdminInfo();
                    queryParam.setAccount(token.getUsername());
                    AdminInfo admin = adminInfoService.getByExample(queryParam);

                    admin.setIp(ip);
                    admin.setToken(IdGenerator.getPrimaryKey().replace("-", ""));
                    admin.setGmtModified(Calendar.getInstance().getTime());

                    queryParam.setId(admin.getId());
                    queryParam.setIp(admin.getIp());
                    queryParam.setToken(admin.getToken());
                    adminInfoService.updateNotNullById(queryParam);
                    logger.info("管理员登录：" + admin.getAccount() + "," + admin.getIp());
                    session.setAttribute("adminLogin", admin);
                    RoleInfo roleInfo = roleInfoService.findOne(admin.getRoleId());
                    if (roleInfo != null) {
                        List<MenuInfo> menuInfos = menuInfoService.listByAssist(new Assist());
                        for (MenuInfo menuInfo : menuInfos) {
                            menuInfo.setHasMenu(RightsHelper.testRights(roleInfo.getRoleRights(),
                                    "" + menuInfo.getId()));
                        }
                        session.setAttribute("Auths", menuInfos);
                    }
                    //登录成功
                    return new Result(false, ResultEnum.SUCCESS.getType(), ResultEnum.SUCCESS.getInfo(),
                            "index1.html");
                } catch (AuthenticationException e) {
                    logger.info(ResultEnum.FAIL.getType(), e);
                    return new Result(true, ResultEnum.FAIL.getType(), "用户名或密码错误", null);
                }
            }
        } catch (Exception e) {
            logger.info(ResultEnum.ERROR.getType(), e);
            return new Result(true, ResultEnum.ERROR.getType(), ResultEnum.ERROR.getInfo(), null);
        }
    }

    /**
     * 用户退出
     *
     * @return
     */
    @RequestMapping(value = "/loginOut", method = RequestMethod.POST)
    public Result loginOut() {
        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        try {
            session.removeAttribute("adminLogin");
            session.removeAttribute("Auths");
            session.removeAttribute("Menus");
            currentUser.logout();
            return new Result(false, ResultEnum.SUCCESS.getType(), ResultEnum.SUCCESS.getInfo(),
                    "login.html");
        } catch (Exception e) {
            logger.info(ResultEnum.ERROR.getType(), e);
            return new Result(true, ResultEnum.ERROR.getType(), ResultEnum.ERROR.getInfo(), null);
        }
    }

    /**
     * 登录后页面检测登录信息
     *
     * @return
     */
    @RequestMapping(value = "/getLoginInfo", method = RequestMethod.POST)
    public Result getLoginInfo() {

        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        AdminInfo adminInfo = null, adminInfo2 = null;
        try {
            adminInfo = (AdminInfo) session.getAttribute("adminLogin");
            //session过期
            if (adminInfo == null) {
                session.removeAttribute("adminLogin");
                session.removeAttribute("Auths");
                session.removeAttribute("Menus");
                currentUser.logout();
                return new Result(true, ResultEnum.NOSESSION.getType(), "会话过期，请重新登录", "login.html");
            }
            //System.out.println(adminInfo);
            adminInfo2 = adminInfoService.findOne(adminInfo.getId());
            if (!adminInfo2.getIp().equals(adminInfo.getIp()) || !adminInfo2.getToken().equals(adminInfo.getToken())) {
                session.removeAttribute("adminLogin");
                session.removeAttribute("Auths");
                session.removeAttribute("Menus");
                currentUser.logout();
                return new Result(true, ResultEnum.OUTBYONE.getType(), ResultEnum.OUTBYONE.getInfo(),
                        "login.html");
            }
            AdminInfo info = new AdminInfo();
            info.setAccount(adminInfo2.getAccount());
            info.setRoleId(adminInfo2.getRoleId());
            return new Result(false, ResultEnum.SUCCESS.getType(), info);
        } catch (Exception e) {
            session.removeAttribute("adminLogin");
            session.removeAttribute("Auths");
            session.removeAttribute("Menus");
            currentUser.logout();
            logger.info(ResultEnum.ERROR.getType(), e);
            return new Result(true, ResultEnum.ERROR.getType(), ResultEnum.ERROR.getInfo(), null);
        }
    }
}
